String 字符串

Posted by 余腾 on 2019-12-06
Estimated Reading Time 4 Minutes
Words 1.1k In Total
Viewed Times

String 字符串

String 重写了 equals() 方法;

因为 object 的 equals 方法是比较的对象的内存地址,String 的 equals() 方法比较的是对象的值。

  • 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用,如果没有就在常量池中重新创建一个 String 对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

常量池 (constant pool)

Java 1.6 及以前,常量池在方法区;

Java 1.7 常量池在堆中的永久代;

Java 1.8 常量池在元空间;

常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。

除了包含代码中所定义的各种基本类型(如int、long等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:

  • 类和接口的全限定名;
  • 字段的名称和描述符;
  • 方法和名称和描述符;

如果是编译期(直接用双引号定义的)已经创建好的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于 equals 相等的字符串,在常量池中永远只有一份,在堆中有多份。

常量池分类

  • Class文件常量池 — 编译阶段
    • 存放字面量符号引用;
  • 运行时常量池 — 方法区
  • 全局字符串常量池
  • 基本类型包装类对象常量池

String str1 = new String();

1
2
3
4
5
String str1 = new String("ab"); //TODO  str1 为一个引用,String对象存储在堆中
String str2 = new String("ab"); //TODO str2 为另一个引用 与对象 str1 的内容一样

str1 == str2 //TODO false 不同对象,地址不相同
str1.equals(str2); //TODO true 内容相同

String s1 = “ab”;

1
2
3
4
5
6
String s1 = "ab"; //TODO 第一次常量池中没有,放在常量池中
String s2 = "ab"; //TODO 第二次从常量池中查找,有的话直接赋值引用
String s3 = "a" + "b";//TODO 两个字符串连接也是字符串,编译期被解析为字符串常量

s1 == s2 //TODO true 同一对象引用,地址相同
s1 == s3 //TODO true

“a” + new String(“b”)

1
2
3
4
String s1 = "ab"; //TODO 放在常量池中
String s4 = "a" + new String("b");//TODO 后半部分无法在编译期确定,创建新String对象

s1 == s4 //TODO flase

引用 String

1
2
3
4
5
6
7
8
String a = "a";
String b = "bb";
String strab = "abb";
String sab = a + b;//TODO 引用的值在编译期无法确定,运行时才可以动态分配赋值给 sab
String strbb = "a" + b;//TODO 引用的值在编译期无法确定

strab == sab //TODO flase
strab == strbb //TODO flase

final String

1
2
3
4
5
6
String strab = "abb";
final String bb = "bb";//TODO 编译时被解析为常量值的一个本地拷贝存储到自己的常量
// "a"+"bb" 等同于 "a" + final String bb
String sbb = "a" + bb;

strab == sbb //TODO true

Method

1
2
3
4
5
6
7
8
String a = "ab";
final String bb = getBB();//TODO 编译期无法确定
String b = "a" + bb;
private static String getBB() {
return "b";
}

a == b //TODO false

String 不可变

每做一次 + 就产生个StringBuffer对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuffer对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuffer 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
String xyz = "x" + "y" + "z";
String xyz1 = "xyz";
String x = "x";
String y = "y";
String z = "z";
String strxyz = x + y + z;//相当于StringBuffer

StringBuffer temp = new StringBuffer();
temp.append(x).append(y).append(z);
String s = temp.toString();

xyz == xyz1 //TODO true
xyz == strxyz //TODO false

因为String不可变,所以 final 对象不能更改指向;至于它所指向的对象的变化,final是不负责的。

1
2
3
4
5
final StringBuffer a = new StringBuffer("111");
final StringBuffer b = new StringBuffer("222");
a=b;//此句编译不通过
final StringBuffer a = new StringBuffer("111");
a.append("222");// 编译通过

StringBuffer/Builder 不可变

1
2
public final class StringBuffer
public final class StringBuilder

StringBuffer 线程安全,方法上有synchronized关键字加锁;适用于多线程
StringBuilder 线程不安全,适用于单线程;

感谢阅读


If you like this blog or find it useful for you, you are welcome to comment on it. You are also welcome to share this blog, so that more people can participate in it. If the images used in the blog infringe your copyright, please contact the author to delete them. Thank you !